from PyQt5.QtQuick import QQuickView
from PyQt5.QtCore import pyqtSignal,pyqtSlot,pyqtProperty,Qt
#include "Frameless/TaoFrameLessView.h"

#include <QGuiApplication>
#include <QQuickItem>
#include <QScreen>
#include <QWindow>

#include <windows.h>

#include <VersionHelpers.h>
#include <WinUser.h>
#include <dwmapi.h>
#include <objidl.h> # Fixes error C2504: 'IUnknown' : base class undefined

#include <windowsx.h>
#include <wtypes.h>
#pragma comment(lib, "Dwmapi.lib") # Adds missing library, fixes error LNK2019: unresolved
#pragma comment(lib, "User32.lib")
#pragma comment(lib, "Gdi32.lib")
# we cannot just use WS_POPUP style
# WS_THICKFRAME: without this the window cannot be resized and so aero snap, de-maximizing and minimizing won't work
# WS_SYSMENU: enables the context menu with the move, close, maximize, minize... commands (shift + right-click on the task bar item)
# WS_CAPTION: enables aero minimize animation/transition
# WS_MAXIMIZEBOX, WS_MINIMIZEBOX: enable minimize/maximize
def isCompositionEnabled():
    composition_enabled = False;
    success = DwmIsCompositionEnabled(composition_enabled) == S_OK;
    return composition_enabled and success;
def selectBorderLessStyle():
    if isCompositionEnabled():
        return Style.aero_borderless
    else:
        return Style.basic_borderless;
def setShadow(handle, enabled):
    if (isCompositionEnabled()):
        # static const MARGINS shadow_state[2] { { 0, 0, 0, 0 }, { 1, 1, 1, 1 } };
        DwmExtendFrameIntoClientArea(handle, shadow_state[enabled]);
def hitTest(winrect, x, y,borderWidth):
    # 鼠标区域位于窗体边框，进行缩放
    if ((x >= winrect.left) and (x < winrect.left + borderWidth) and (y >= winrect.top) and (y < winrect.top + borderWidth)):
        return HTTOPLEFT;
    elif (x < winrect.right and x >= winrect.right - borderWidth and y >= winrect.top and y < winrect.top + borderWidth):
        return HTTOPRIGHT;
    elif (x >= winrect.left and x < winrect.left + borderWidth and y < winrect.bottom and y >= winrect.bottom - borderWidth):
        return HTBOTTOMLEFT;
    elif (x < winrect.right and x >= winrect.right - borderWidth and y < winrect.bottom and y >= winrect.bottom - borderWidth):
        return HTBOTTOMRIGHT;
    elif (x >= winrect.left and x < winrect.left + borderWidth):
        return HTLEFT;
    elif (x < winrect.right and x >= winrect.right - borderWidth):
        return HTRIGHT;
    elif (y >= winrect.top and y < winrect.top + borderWidth):
        return HTTOP;
    elif (y < winrect.bottom and y >= winrect.bottom - borderWidth):
        return HTBOTTOM;
    else:
        return 0;

def isMaxWin( win):
    return win.windowState() == Qt.WindowMaximized;
def isFullWin(win):
    return win.windowState() == Qt.WindowFullScreen;
class Style:
    pass
    # windowed = WS_OVERLAPPEDWINDOW | WS_THICKFRAME | WS_CAPTION | WS_SYSMENU | WS_MAXIMIZEBOX | WS_MINIMIZEBOX,
    # aero_borderless = WS_POPUP | WS_THICKFRAME | WS_CAPTION | WS_SYSMENU | WS_MAXIMIZEBOX | WS_MINIMIZEBOX,
    # basic_borderless = WS_POPUP | WS_THICKFRAME | WS_SYSMENU | WS_MAXIMIZEBOX | WS_MINIMIZEBOX

class TaoFrameLessView(QQuickView):
    def __init__(self,parent=None):
        super().__init__(parent)    
        self.m_firstRun = True;
        self.m_isMax = False;
        self.m_isFull = False;
        self.m_titleItem = None
        self.mMenuHandler = None
        self.borderless = True;        # is the window currently borderless
        self.borderless_resize = True; # should the window allow resizing by dragging the borders while borderless
        self.borderless_drag = True;   # should the window allow moving my dragging the client area
        self.borderless_shadow = True; # should the window display a native aero shadow while borderless
        # 此处不需要设置flags
        self.setFlags(Qt.CustomizeWindowHint | Qt.Window | Qt.FramelessWindowHint | Qt.WindowMinMaxButtonsHint | Qt.WindowTitleHint |            Qt.WindowSystemMenuHint);
        # self.setResizeMode(self.SizeRootObjectToView);

        # self.setIsMax(windowState() == Qt.WindowMaximized);
        # self.setIsFull(windowState() == Qt.WindowFullScreen);
        # self.windowStateChanged.connect(self.windowState_func)
    def windowState_func(self,state):
        self.setIsMax(windowState() == Qt.WindowMaximized)
        self.setIsFull(windowState() == Qt.WindowFullScreen)

    # def setBorderLess(HWND handle, bool enabled):
    #     auto newStyle = enabled ? selectBorderLessStyle() : Style.windowed;
    #     auto oldStyle = static_cast<Style>(.GetWindowLongPtrW(handle, GWL_STYLE));
    #     if (oldStyle != newStyle)
    #     {
    #         borderless = enabled;
    #         .SetWindowLongPtrW(handle, GWL_STYLE, static_cast<LONG>(newStyle));

    #         # when switching between borderless and windowed, restore appropriate shadow state
    #         setShadow(handle, borderless_shadow and (newStyle != Style.windowed));

    #         # redraw frame
    #         .SetWindowPos(handle, nullptr, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE);
    #         .ShowWindow(handle, SW_SHOW);
    #     }
    # }
    # void setBorderLessShadow(HWND handle, bool enabled)
    # {
    #     if (borderless)
    #     {
    #         borderless_shadow = enabled;
    #         setShadow(handle, enabled);
    #     }
    # }

    mousePressed=pyqtSignal()
    def moveToScreenCenter(self):
        pass
    @pyqtSlot()
    def setTitleItem(self):
        pass
    isMaxChanged=pyqtSignal()
    @pyqtProperty(bool, notify=isMaxChanged)
    def isMax(self):
        return False

# }
# void TaoFrameLessView.showEvent(QShowEvent* e)
# {
#     if (d.m_firstRun)
#     {
#         d.m_firstRun = False;
#         # 第一次show的时候，设置无边框。不在构造函数中设置。取winId会触发QWindowsWindow.create,直接创建win32窗口,引起错乱(win7 或者虚拟机启动即黑屏)。
#         d.setBorderLess((HWND)(winId()), d.borderless);
#         {
#             # Qt 5.15.2 的bug; 问题复现及解决方法：当使用WM_NCCALCSIZE 修改非客户区大小后，移动窗口到其他屏幕时，qwindows.dll 源码 qwindowswindow.cpp:2447
#             # updateFullFrameMargins() 函数 处会调用qwindowswindow.cpp:2453 的
#             # calculateFullFrameMargins函数重新获取默认的非客户区大小，导致最外层窗口移动屏幕时会触发resize消息，引起40像素左右的黑边；故此处创建Menu
#             # 使其调用qwindowswindow.cpp:2451 的 QWindowsContext.forceNcCalcSize() 函数计算非客户区大小

#             # 已知负面效果: 引入win32 MENU后，Qt程序中如果有alt开头的快捷键，会不生效，被Qt滤掉了，需要修改Qt源码
#             #  QWindowsKeyMapper.translateKeyEventInternal 中的
#             #  if (msgType == WM_SYSKEYDOWN and (nModifiers & AltAny) != 0 and GetMenu(msg.hwnd) != nullptr)
#             #   return False;
#             #  这两行屏蔽掉

#             d.mMenuHandler = .CreateMenu();
#             .SetMenu((HWND)winId(), d.mMenuHandler);
#         }
#     }
#     Super.showEvent(e);
# }
# TaoFrameLessView.~TaoFrameLessView()
# {
#     if (d.mMenuHandler != NULL)
#     {
#         .DestroyMenu(d.mMenuHandler);
#     }
#     delete d;
# }

# bool TaoFrameLessView.isMax() const
# {
#     return d.m_isMax;
# }

# bool TaoFrameLessView.isFull() const
# {
#     return d.m_isFull;
# }
# QQuickItem* TaoFrameLessView.titleItem() const
# {
#     return d.m_titleItem;
# }
# void TaoFrameLessView.setTitleItem(QQuickItem* item)
# {
#     d.m_titleItem = item;
# }
# QRect TaoFrameLessView.calcCenterGeo(const QRect& screenGeo, const QSize& normalSize)
# {
#     int w = normalSize.width();
#     int h = normalSize.height();
#     int x = screenGeo.x() + (screenGeo.width() - w) / 2;
#     int y = screenGeo.y() + (screenGeo.height() - h) / 2;
#     if (screenGeo.width() < w)
#     {
#         x = screenGeo.x();
#         w = screenGeo.width();
#     }
#     if (screenGeo.height() < h)
#     {
#         y = screenGeo.y();
#         h = screenGeo.height();
#     }

#     return { x, y, w, h };
# }
# void TaoFrameLessView.moveToScreenCenter()
# {
#     auto geo = calcCenterGeo(screen().availableGeometry(), size());
#     if (minimumWidth() > geo.width() || minimumHeight() > geo.height())
#     {
#         setMinimumSize(geo.size());
#     }
#     setGeometry(geo);
#     update();
# }

# void TaoFrameLessView.setIsMax(bool isMax)
# {
#     if (d.m_isMax == isMax)
#         return;

#     d.m_isMax = isMax;
#     emit isMaxChanged(d.m_isMax);
# }
# void TaoFrameLessView.setIsFull(bool isFull)
# {
#     if (d.m_isFull == isFull)
#         return;

#     d.m_isFull = isFull;
#     emit isFullChanged(d.m_isFull);
# }
# void TaoFrameLessView.resizeEvent(QResizeEvent* e)
# {
#     # SetWindowRgn(HWND(winId()), CreateRoundRectRgn(0, 0, width(), height(), 4, 4), True);
#     Super.resizeEvent(e);
# }

# #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
# bool TaoFrameLessView.nativeEvent(const QByteArray& eventType, void* message, qintptr* result)
# #else
# bool TaoFrameLessView.nativeEvent(const QByteArray& eventType, void* message, long* result)
# #endif

# {
#     const long border_width = 4;
#     if (!result)
#     {
#         # 防御式编程
#         # 一般不会发生这种情况，win7一些极端情况，会传空指针进来。解决方案是升级驱动、切换到basic主题。
#         return False;
#     }

# #if (QT_VERSION == QT_VERSION_CHECK(5, 11, 1))
#     # Work-around a bug caused by typo which only exists in Qt 5.11.1
#     const auto msg = *reinterpret_cast<MSG**>(message);
# #else
#     const auto msg = static_cast<LPMSG>(message);
# #endif

#     if (!msg || !msg.hwnd)
#     {
#         return False;
#     }
#     switch (msg.message)
#     {
#     case WM_NCCALCSIZE: {
# #if 1
#         const auto mode = static_cast<BOOL>(msg.wParam);
#         const auto clientRect = mode ? &(reinterpret_cast<LPNCCALCSIZE_PARAMS>(msg.lParam).rgrc[0]) : reinterpret_cast<LPRECT>(msg.lParam);
#         if (mode == True and d.borderless)
#         {
#             *result = WVR_REDRAW;
#             # 规避 拖动border进行resize时界面闪烁
#             if (!isMaxWin(this) and !isFullWin(this))
#             {
#                 if (clientRect.top != 0)
#                 {
#                     clientRect.top -= 0.1;
#                 }
#             }
#             else
#             {
#                 if (clientRect.top != 0)
#                 {
#                     clientRect.top += 0.1;
#                 }
#             }
#             return True;
#         }
# #else
#         *result = 0;
#         return True;
# #endif
#         break;
#     }
#     case WM_NCACTIVATE: {
#         if (!isCompositionEnabled())
#         {
#             # Prevents window frame reappearing on window activation
#             # in "basic" theme, where no aero shadow is present.
#             *result = 1;
#             return True;
#         }
#         break;
#     }
#     case WM_NCHITTEST: {
#         if (d.borderless)
#         {
#             RECT winrect;
#             GetWindowRect(HWND(winId()), &winrect);

#             long x = GET_X_LPARAM(msg.lParam);
#             long y = GET_Y_LPARAM(msg.lParam);

#             *result = 0;
#             if (!isMaxWin(this) and !isFullWin(this))
#             { # 非最大化、非全屏时，进行命中测试，处理边框拖拽
#                 *result = hitTest(winrect, x, y, border_width);
#                 if (0 != *result)
#                 {
#                     return True;
#                 }
#             }

#             if (d.m_titleItem)
#             {
#                 auto titlePos = d.m_titleItem.mapToGlobal({ 0, 0 });
#                 titlePos = mapFromGlobal(titlePos.toPoint());
#                 auto titleRect = QRect(titlePos.x(), titlePos.y(), d.m_titleItem.width(), d.m_titleItem.height());
#                 double dpr = qApp.devicePixelRatio();
#                 QPoint pos = mapFromGlobal(QPoint(x / dpr, y / dpr));
#                 if (titleRect.contains(pos))
#                 {
#                     *result = HTCAPTION;
#                     return True;
#                 }
#             }
#             return False;
#         }
#         break;
#     } # end case WM_NCHITTEST
#     }
#     return Super.nativeEvent(eventType, message, result);
